package com.magicrule.car.systemManage.configClass.shiro;

import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;

import com.magicrule.car.systemManage.model.User;

/**
 * 并发控制登录人数
 * @author hebinbin
 *
 */
public class KickoutSessionControlFilter extends AccessControlFilter {

	
	private String kickoutUrl; //踢出后到的地址 
	
	private Boolean kickoutAfter=false; //踢出之前登录的/之后登录的用户(默认踢出之前登录的用户)
	
	private int maxSession=1; //同一个帐号最大会话数(默认1)
	
	private SessionManager sessionManager; //用于根据会话 ID，获取会话进行踢出操作的；
	
	private Cache<String, Deque<Serializable>> cache; //cache来缓存用户登录的会话；用于保存用户—会话之间的关系的；

	
	public String getKickoutUrl() {
		return kickoutUrl;
	}

	public void setKickoutUrl(String kickoutUrl) {
		this.kickoutUrl = kickoutUrl;
	}

	public Boolean getKickoutAfter() {
		return kickoutAfter;
	}

	public void setKickoutAfter(Boolean kickoutAfter) {
		this.kickoutAfter = kickoutAfter;
	}

	public int getMaxSession() {
		return maxSession;
	}

	public void setMaxSession(int maxSession) {
		this.maxSession = maxSession;
	}

	public SessionManager getSessionManager() {
		return sessionManager;
	}

	public void setSessionManager(SessionManager sessionManager) {
		this.sessionManager = sessionManager;
	}

	public Cache<String, Deque<Serializable>> getCache() {
		return cache;
	}

	public void setCache(CacheManager cacheManager) {
		this.cache = cacheManager.getCache("shiro-kickout-session");
	}

	
	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
			throws Exception {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		Subject subject = getSubject(request, response);
		
		 /* if(!subject.isAuthenticated() && !subject.isRemembered()) {
		        //如果没有登录，直接进行之后的流程
		        return true;
		    }*/
		  
		//如果没有登录，直接退出
	    if(!subject.isAuthenticated() && !subject.isRemembered()) {
	    	 //调用saveRequest方法来将之前的请求存到session中
	    	saveRequest(request);
	        WebUtils.issueRedirect(request, response, kickoutUrl);
	        return false;
	    }
	    
	    Session session = subject.getSession();
	    User user = (User) subject.getPrincipal();
	    String username=user.getUserName();
	    Serializable sessionId = session.getId();
	    //同步控制
	    Deque<Serializable> deque = cache.get(username);
	    if(deque==null || deque.size()==0) {
	        deque = new LinkedList<Serializable>();
	    }
	    //如果队列里没有此sessionId，且用户没有被踢出；放入队列
	    if(!deque.contains(sessionId) && session.getAttribute("kickout") == null) {
	        deque.push(sessionId);
	    }
	    //如果队列里的sessionId数超出最大会话数，开始踢人
	    while(deque.size() > maxSession) {
	        Serializable kickoutSessionId = null;
	        if(kickoutAfter) { //如果踢出后者
	            kickoutSessionId = deque.removeFirst();
	        } else { //否则踢出前者
	            kickoutSessionId = deque.removeLast();
	        }
	        try {
	            Session kickoutSession =
	                sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
	            if(kickoutSession != null) {
	                //设置会话的kickout属性表示踢出了
	                kickoutSession.setAttribute("kickout", true);
	            }
	        } catch (Exception e) {//ignore exception
	        }
	    }
	    //如果被踢出了，直接退出，重定向到踢出后的地址
	    if (session.getAttribute("kickout") != null) {
	        //会话被踢出了
	        try {
	        	 subject.logout();
	        } catch (Exception e) { //ignore
	        	
	        }
	        //调用saveRequest方法来将之前的请求存到session中
	        saveRequest(request);
	        WebUtils.issueRedirect(request, response, kickoutUrl);
	        return false;
	    }
	    //能运行到这一步说明没有被踢出，保存deque到缓存中
	    cache.put(username, deque);
	    return true;
	    
	}
	

}
